<template>
  <div>
    <div>
      <RHButton
        :btn="item"
        :key="index.toString() + 1"
        @onclick="upload(item.param)"
        v-for="(item, index) in button"
      ></RHButton>
    </div>
    <div>
      <uploader
        :autoStart="autoStart"
        :options="options"
        @file-added="fileAdded"
        @file-error="fileError"
        @file-progress="fileProgress"
        @file-success="fileSuccess"
        class="uploader-example"
        ref="uploader"
      >
        <uploader-btn
          :attrs="{ accept: acceptConfig.getAll() }"
          :directory="false"
          :single="false"
          id="uploader-btn_0"
          style="display: none;"
        ></uploader-btn>
        <uploader-btn
          :attrs="{ accept: acceptConfig.image }"
          :directory="false"
          :single="false"
          id="uploader-btn_1"
          style="display: none;"
        ></uploader-btn>
        <uploader-btn
          :attrs="{ accept: acceptConfig.getAll() }"
          :directory="true"
          :single="false"
          id="uploader-btn_2"
          style="display: none;"
        ></uploader-btn>
      </uploader>
    </div>
    <div>
      <!--            <RHTable :RHTable="table" ref="file_table"></RHTable>-->
      <el-table
        :data="table.data"
        style="width: 100%"
        v-if="table.data.length > 0"
      >
        <el-table-column prop="name" label="文件名"> </el-table-column>
        <el-table-column label="">
          <template slot-scope="scope">
            <RHImg :RHImg="scope.row"></RHImg>
          </template>
        </el-table-column>
        <el-table-column label="状态" :formatter="fileState"> </el-table-column>
        <el-table-column :formatter="fileSize" label="文件大小">
        </el-table-column>
        <el-table-column label="进度" prop="stateProgress">
          <template slot-scope="scope">
            <el-progress
              :percentage="scope.row.stateProgress"
              :stroke-width="24"
              :text-inside="true"
              status="success"
              v-if="scope.row.status === 1"
            ></el-progress>
            <el-progress
              :percentage="scope.row.stateProgress"
              :stroke-width="24"
              :text-inside="true"
              status="exception"
              v-else-if="scope.row.status === 2"
            ></el-progress>
            <el-progress
              :percentage="scope.row.stateProgress"
              :stroke-width="24"
              :text-inside="true"
              v-else
            ></el-progress>
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template slot-scope="scope">
            <el-button
              type="danger"
              icon="el-icon-delete"
              circle
              @click="cancel(scope.row, scope.$index)"
            ></el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>
<script>
//https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md
//https://github.com/simple-uploader/vue-uploader/blob/master/README_zh-CN.md
import RHButton from "@/common/button/index.vue";
import RHImg from "@/common/img/index.vue";
import { ACCEPT_CONFIG } from "./config";
import _ from "lodash";
import _uuid from "lodash-uuid";
import SparkMD5 from "spark-md5";

export default {
  name: "file_index",
  data: function() {
    return {
      data: {
        key: "",
        imgBtn: {
          show: false,
          value: "选择图片"
        },
        fileBtn: {
          show: false,
          value: "选择文件"
        },
        folderBtn: {
          show: false,
          value: "选择文件夹"
        },
        success: function() {},
        error: function() {}
      },
      acceptConfig: ACCEPT_CONFIG,
      btn: {
        attrs: {
          accept: "image/*"
        },
        single: false,
        directory: false
      },
      button: [],
      param: {},
      options: {
        target: "/api/file/api/file/file/chunkUpload", //地址
        testChunks: true, //分片上传检查  实现秒传  跨浏览器上传
        checkChunkUploadedByResponse: this.checkChunkUploadedByResponse,
        singleFile: false, //单文件上传
        chunkSize: 1024 * 1024 * 3, // 分块大小 单位 b
        forceChunkSize: false, // 是否强制所有的块都是小于等于 chunkSize 的值。默认是 false。
        simultaneousUploads: 3, //并发上传数
        fileParameterName: "file", //上传时文件从参数名称
        headers: this.headers, //额外的一些请求头，如果是函数的话，则会传入 Uploader.File 实例、当前块 Uploader.Chunk 以及是否是测试模式，默认 {}
        allowDuplicateUploads: false, // 如果说一个文件以及上传过了是否还允许再次上传。默认的话如果已经上传了，除非你移除了否则是不会再次重新上传的，所以也就是默认值为 false。
        prioritizeFirstAndLastChunk: false, // 对于文件而言是否高优先级发送第一个和最后一个块。一般用来发送到服务端，然后判断是否是合法文件；例如图片或者视频的 meta 数据一般放在文件第一部分，这样可以根据第一个块就能知道是否支持；默认 false。
        preprocess: function(chunk) {
          //可选的函数，每个块在测试以及上传前会被调用
          chunk.preprocessFinished();
        },
        query: {}
      },
      autoStart: false, //选择文件后自动上传
      attrs: {
        accept: ACCEPT_CONFIG.getAll()
      },
      state: {
        md5: "效验md5",
        success: "上传成功",
        error: "上传失败",
        start: "开始上传",
        middle: "上传中"
      },
      file: {
        state: "md5",
        stateProgress: 0, //上传进度
        status: 0, //   0 正在上传 1 上传完成 2 失败/出错
        fileId: null
      },
      fileList: [],
      table: {
        data: [],
        columns: [
          { fieId: "name", title: "文件名" },
          { title: "状态", formatter: this.fileState },
          { title: "文件大小", formatter: this.fileSize },
          {
            fieId: "stateProgress",
            title: "当前进度",
            type: "progress",
            progress: { status: "status" }
          }
        ]
      }
    };
  },
  methods: {
    fileState(row) {
      return this.state[row.state];
    },
    fileSize(row) {
      if (_.toSafeInteger(row.size / 1024 / 1024) > 0) {
        return _.toSafeInteger(row.size / 1024 / 1024) + "MB";
      } else if (_.toSafeInteger(row.size / 1024) > 0) {
        return _.toSafeInteger(row.size / 1024) + "KB";
      } else {
        return _.toSafeInteger(row.size) + "K";
      }
    },
    //暂停
    filePause(file) {
      if (file.paused) {
        file.resume();
      } else {
        file.pause();
      }
    },
    cancel(file, index) {
      if(file.cancel){
        file.cancel();
      }
      this.table.data = _.remove(this.table.data, function(n, i) {
        return i !== index;
      });
    },
    upload(param) {
      //触发上传文件单击事件
      $("#uploader-btn_" + param.index).click();
    },
    /**
     * 请求头
     * @param file
     * @param chunk
     * @param flag 是否时测试
     * @returns {{token: *}}
     */
    headers: function(file, chunk, flag) {
      /*console.log("file:"+JSON.stringify(file));
                              console.log("chunk:"+JSON.stringify(chunk));*/
      return {
        token: this.$store.state.token
      };
    },
    /**
     * 添加了一个文件事件，一般用做文件校验，如果设置 file.ignored = true 的话这个文件就会被过滤掉。
     */
    fileAdded(file) {
      this.computeMD5(file);
    },
    /**
     *  添加了一批文件事件，一般用做一次选择的多个文件进行校验，如果设置 files.ignored = true 或者 fileList.ignored = true 的话本次选择的文件就会被过滤掉。
     * @param files
     * @param fileList
     */
    filesAdded(files, fileList) {
      console.log("files", files);
      console.log("fileList", fileList);
    },
    /**
     * md5
     */
    computeMD5(file) {
      _.defaultsDeep(file, this.file);
      this.table.data.push(file);
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      file.pause();
      loadNext();
      fileReader.onload = e => {
        spark.append(e.target.result);
        if (currentChunk < chunks) {
          currentChunk++;
          loadNext();
          // 实时展示MD5的计算进度
          file.stateProgress = _.toSafeInteger((currentChunk / chunks) * 100);
          this.updateFileList(file, {
            stateProgress: _.toSafeInteger(file.progress() * 100)
          });
        } else {
          let md5 = spark.end();
          if (!file.isFolder) {
            file.uniqueIdentifier = md5;
            file["state"] = "start";
            file["status"] = 0;
            file.resume();
          }
        }
      };
      fileReader.onerror = function() {
        this.$util.message(
          "文件" + { state: 0, msg: file.name + "读取出错，请检查该文件" }
        );
        file.cancel();
      };

      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;
        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },
    /**
     * 获取uuID
     */
    getUUID() {
      return _.join(_.split(_uuid.uuid(), "-"), "");
    },
    /**
     *  一个文件上传成功事件，第一个参数 rootFile 就是成功上传的文件所属的根 Uploader.File 对象，它应该包含或者等于成功上传文件；
     *  第二个参数 file 就是当前成功的 Uploader.File 对象本身；
     *  第三个参数就是 message 就是服务端响应内容，永远都是字符串；
     *  第四个参数 chunk 就是 Uploader.Chunk 实例，它就是该文件的最后一个块实例，如果你想得到请求响应码的话，chunk.xhr.status 就是。
     */
    fileSuccess(rootFile, file, message, chunk) {
      let data = JSON.parse(message);
      if (data.msg === "GET") {
        this.$util.message({ state: 1, msg: file.name + "秒传" });
        this.updateFileList(
          file.uniqueIdentifier,
          {
            state: "success",
            status: 1,
            fileId: data.entity.fileId
          },
          data
        );
      } else {
        if (data.state === 1) {
          this.$util.message({ state: 1, msg: file.name + "上传成功" });

          this.updateFileList(
            file.uniqueIdentifier,
            {
              state: "success",
              status: 1,
              fileId: data.entity.fileId
            },
            data
          );
        } else {
          this.$util.message({ state: 0, msg: file.name + "上传失败" });
          this.updateFileList(
            file.uniqueIdentifier,
            {
              state: "error",
              status: 2,
              fileId: data.entity.fileId
            },
            data
          );
        }
      }
      this.data.success(message, rootFile, file, chunk, this.data.key);
    },
    checkChunkUploadedByResponse(chunk, response) {
      let data = JSON.parse(response).entity;
      let identifier = data.md5;
      let file = this.uploader().getFromUniqueIdentifier(identifier);
      identifier = data.fileId + "," + data.md5;
      if (_.isEmpty(file)) {
        file = this.uploader().getFromUniqueIdentifier(identifier);
      }
      file.uniqueIdentifier = identifier;
      this.param[identifier] = {
        path: data.url
      };
      this.uploader().opts.query = this.param[identifier];
      if (data.complete === 1) {
        return true;
      }
      return (
        (data.fileDetailsChunkNumbers || []).indexOf(chunk.offset + 1) >= 0
      );
    },
    fileProgress(rootFile, file, chunk) {
      if (file.isUploading()) {
        this.updateFileList(file.uniqueIdentifier, {
          state: "middle",
          status: 0
        });
      }
      if (file.isComplete()) {
        this.updateFileList(file.uniqueIdentifier, {
          state: "success",
          status: 1
        });
      }
      this.updateFileList(file.uniqueIdentifier, {
        stateProgress: _.toSafeInteger(file.progress() * 100)
      });
    },
    /**
     * 文件上传出错
     */
    fileError(rootFile, file, response, chunk) {
      this.updateFileList(file.uniqueIdentifier, { state: "error", status: 2 });
      file.pause();
      this.$util.message("文件" + { state: 0, msg: file.name + "上传出错" });
      this.data.error(response, rootFile, file, chunk);
    },
    uploader() {
      return this.$refs.uploader.uploader;
    },
    updateFileList(id, param, entity) {
      let data = this.table.data;
      this.table.data = [];
      data.forEach((item, index) => {
        if (item.uniqueIdentifier === id) {
          if (!_.isNil(param.state)) {
            item.state = param.state;
          }
          if (!_.isNil(param.stateProgress)) {
            item.stateProgress = param.stateProgress;
          }
          if (!_.isNil(param.status)) {
            item.status = param.status;
          }
          if (!_.isNil(param.fileId)) {
            item.fileId = param.fileId;
          }
          if (entity) {
            item.url = entity.entity.url;
            item.md5 = entity.entity.md5;
            item.totalSize = entity.entity.totalSize;
          }
        }
        this.table.data.push(item);
      });
    },
    //初始化
    initialize() {
      this.data = Object.assign(this.data, this.fileData);
      this.table.data = Object.assign(this.tableData, {});
      if (this.data.imgBtn.show) {
        this.button.push({
          value:
            this.data.imgBtn.value != null
              ? this.data.imgBtn.value
              : "选择图片",
          icon: "el-icon-picture-outline",
          param: { index: 1 },
          click: this.upload
        });
      }
      if (this.data.fileBtn.show) {
        this.button.push({
          value:
            this.data.fileBtn.value != null
              ? this.data.fileBtn.value
              : "选择文件",
          icon: "el-icon-folder",
          param: { index: 0 },
          click: this.upload
        });
      }
      if (this.data.folderBtn.show) {
        this.button.push({
          value:
            this.data.folderBtn.value != null
              ? this.data.folderBtn.value
              : "选择文件夹",
          icon: "el-icon-folder-opened",
          param: { index: 2 },
          click: this.upload
        });
      }
    }
  },
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不！能！获取组件实例 `this`
    // 因为当守卫执行前，组件实例还没被创建
    next();
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变，但是该组件被复用时调用
    // 举例来说，对于一个带有动态参数的路径 /foo/:id，在 /foo/1 和 /foo/2 之间跳转的时候，
    // 由于会渲染同样的 Foo 组件，因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
    next();
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    let flag = false;
    this.table.data.forEach((item, index) => {
      if (item.status === 0) {
        flag = true;
      }
    });
    if (flag) {
      this.$confirm("正在上传文件，离开页面将终止上传！", "提示", {
        confirmButtonText: "离开",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          next();
        })
        .catch(() => {
          next(false);
        });
    } else {
      next();
    }
  },
  created() {
    this.initialize();
  },
  components: {
    RHButton,
    RHImg
  },
  props: {
    fileData: {},
    tableData: {
      type: Array
    }
  }
};
</script>

<style scoped></style>
